package com.fp.m7.simulation;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import org.apache.commons.lang.time.StopWatch;

import com.fp.m7.api.manager.AccountManager;
import com.fp.m7.api.manager.CommentManager;
import com.fp.m7.api.manager.FollowManager;
import com.fp.m7.generator.Random;
import com.fp.m7.generator.TestDataGenerator;
import com.fp.m7.performance.PerformanceTester;
import com.fp.m7.util.MockClock;

public class MicroblogSimulator {
	private static final Logger logger = Logger
			.getLogger(MicroblogSimulator.class.getName());

	private final static int ACCOUNT = 5;
	private final static int COMMENT = ACCOUNT + 50;
	private final static int TIMELINE = COMMENT + 150;
	private final static int BLOG = TIMELINE + 100;
	private final static int FOLLOW = BLOG + 25;
	private final static int UNFOLLOW = FOLLOW + 5;
	private final static int UPDATE = UNFOLLOW + 20;
	private final static int FOLLOWERS = UPDATE + 150;
	private final static int SUM = FOLLOWERS;

	private int accountCount, commentCount, timelineCount, blogCount,
			followCount, unfollowCount, updateCount, followersCount;

	protected final MockClock clock;
	protected final Random random;

	private final TestDataGenerator generator;
	private final PerformanceTester tester;

	public MicroblogSimulator(AccountManager accountManager,
			CommentManager commentManager, FollowManager followManager,
			MockClock clock, Random random) {
		this.clock = clock;
		this.random = random;
		this.generator = new TestDataGenerator(accountManager, commentManager,
				followManager, clock, random);
		this.tester = new PerformanceTester(accountManager, commentManager,
				followManager, random);
	}

	public void simulate(int iterations) {
		List<Serializable> accountIds = new ArrayList<Serializable>();

		logger.info(String.format(
				"Starting microblog simulation scheduled for %d iterations.",
				iterations));

		initialData(accountIds);

		clock.configure(TestDataGenerator.BEGIN_DATE,
				TestDataGenerator.END_DATE, iterations);

		StopWatch total = new StopWatch();
		total.start();
		for (int i = 0; i < iterations; ++i) {
			int pickedNumber = random.nextInt(SUM);
			if (pickedNumber < ACCOUNT) {
				generator.newAccount(accountIds);
				++accountCount;
			} else if (pickedNumber < COMMENT) {
				generator.newComment(accountIds);
				++commentCount;
			} else if (pickedNumber < TIMELINE) {
				tester.commentFeed(1, accountIds);
				++timelineCount;
			} else if (pickedNumber < BLOG) {
				tester.commentList(1, accountIds);
				++blogCount;
			} else if (pickedNumber < FOLLOW) {
				generator.follow(accountIds);
				++followCount;
			} else if (pickedNumber < UNFOLLOW) {
				generator.unfollow(accountIds);
				++unfollowCount;
			} else if (pickedNumber < UPDATE) {
				generator.updateAccount(accountIds);
				++updateCount;
			} else if (pickedNumber < FOLLOWERS) {
				tester.followers(1, accountIds);
				++followersCount;
			}
		}

		logger.info(String
				.format("Simulation process took %s in overall.\n"
						+ "Generated %d accounts, %d comments, %d follows, %d unfollows, %d updates, %d timelines, %d blog pages, %d followers' listings.",
						total, accountCount, commentCount, followCount,
						unfollowCount, updateCount, timelineCount, blogCount,
						followersCount));
	}

	private void initialData(List<Serializable> accountIds) {
		int initialAccountNumber = 10;
		int initialCommentsNumber = 100;

		logger.info(String.format(
				"Generating %d initial accounts and %d initial comments.",
				initialAccountNumber, initialCommentsNumber));

		StopWatch stopWatch = new StopWatch();
		stopWatch.start();

		clock.configure(TestDataGenerator.BEGIN_DATE,
				TestDataGenerator.END_DATE, initialAccountNumber);
		generator.createAccounts(initialAccountNumber, accountIds);

		clock.configure(TestDataGenerator.BEGIN_DATE,
				TestDataGenerator.END_DATE, initialCommentsNumber);
		generator.createComments(initialCommentsNumber, accountIds);

		logger.info(String.format("Finished generating initial data in %s.",
				stopWatch));
	}
}
